/* eslint-disable max-lines-per-function */
import { computed, ref } from "vue";

export interface ResourceOptions<T> {
  initValue?: T;
  immediately?: boolean;
  /** 填写依赖元素，依赖元素发生改变，则会自动更新 */
  deps?: unknown;
  refetch?: RefetchOption;
  tap?: (data: T) => void;
}
export interface RefetchOption {
  /** 如果发生了异步函数覆盖，进行警告*/
  warn?: boolean;
  /** 如果发生了异步函数覆盖，并且需要撤销操作的时候使用 */
  cancelCallback?: (p: Promise<boolean>) => void;
}
/**
 * @zh 安全获取异步数据并返回状态
 * @example
 * const { data, loading: infoLoading,  refetch: searchOrder } = useResource(
 *    () => {
 *       return getOrderAndTaskDetailsApi({orderNo: orderNo.value}).then(res => {
 *           return res && +res.code === 0 ? res :  null;
 *       });},
 *   {
 *       immediately: false,
 *   });
 *
 */
export const useResource = <T, D extends Array<unknown>>(
  fetcher: (...args: D) => Promise<T>,
  {
    initValue = null as unknown as any,
    immediately = false,
    // eslint-disable-next-line @typescript-eslint/no-empty-function
    tap: tapFn = () => {},
  }: ResourceOptions<T> = {}
) => {
  const data = ref<T>(initValue);
  const loading = ref(false);
  const error = ref<Error | false>(false);
  const isReady = computed(() => !!(!loading.value && !error.value));
  let p!: Promise<T>;

  const refetch = (...args: Parameters<typeof fetcher>) => {
    // 上一次请求尚未结束
    if (!isReady.value) {
      // eslint-disable-next-line no-console
      console.warn(
        "Resource Atom: some fetch has been covered; Recommend to add a cancelCallback to some Hook"
      );
    }

    loading.value = true;
    const tempP = fetcher(...args)
      .then((res) => {
        data.value = res;
        error.value = false;

        // 当自己没有被 cancel 时，进行 tap 函数
        if (tempP === p) tapFn(res);
        return res;
      })
      .catch((err) => {
        error.value = err;
        // 直接抛出异常
        throw err;
      })
      .finally(() => {
        loading.value = false;
      });
    p = tempP;
    return tempP;
  };
  /** @ts-ignore */
  immediately && refetch();
  return {
    data,
    error,
    loading,
    isReady,
    refetch,
    promise: () => {
      return p;
    },
  };
};
